// 创建 WxRequest 类，采用类的方式进行封装会让方法更具有复用性，也可以方便进行添加新的属性和方法
class WxRequest {

  // 默认参数对象
  defaults = {
    baseURL: '', // 请求基准地址
    url: '', // 开发者服务器接口地址
    data: null, // 请求参数
    method: 'GET', // 默认请求方法
    // 请求头
    header: {
      'Content-type': 'application/json' // 设置数据的交互格式
    },
    isLoading: true, // 是否显示 loading 提示框
    timeout: 60000 // 小程序默认超时时间是 60000，一分钟
  }

  // 定义拦截器对象，包含请求拦截器和响应拦截器方法，方便在请求或响应之前进行处理。
  interceptors = {
    // 请求拦截器
    request: (config) => config,
    // 响应拦截器
    response: (response) => response
  }

  // 定义数组队列
  // 初始值需要是一个空数组，用来存储请求队列、存储请求标识
  queue = []

  /**
   * @description 定义 constructor 构造函数，用于创建和初始化类的属性和方法
   * @param {*} params 用户传入的请求配置项
   */
  constructor(params = {}) {
    // 使用 Object.assign 合并默认参数以及传递的请求参数
    this.defaults = Object.assign({}, this.defaults, params)
  }

  /**
   * @description 发起请求的方法
   * @param { Object} options 请求配置选项，同 wx.request 请求配置选项
   * @returns Promise
   */
  request(options) {

    // 如果有新的请求 就清空
    this.timerId && clearTimeout(this.timerId)

    // 拼接完整的请求地址
    options.url = this.defaults.baseURL + options.url
    // 合并请求参数
    options = {
      ...this.defaults,
      ...options
    }

    if (options.isLoading && options.method !== 'UPLOAD') {
      // 在请求发送之前，添加loading效果
      if (this.queue.length === 0) {
        wx.showLoading({
          title: '数据加载中...'
        })
      }
      // 然后向队列中添加 request 标识，代表需要发送一次新请求
      this.queue.push('request')
    }

    // 请求发送之前，调用请求拦截器，新增修改请求参数
    options = this.interceptors.request(options)

    // 方法返回一个 Promise 对象
    return new Promise((resolve, reject) => {
      if (options.method === 'UPLOAD') {
        wx.uploadFile({
          ...options,
          success: (res) => {
            // 将服务器响应的数据通过 JSON.parse 转换为 JS 对象
            res.data = JSON.parse(res.data)

            // 合并参数
            const mergeRes = Object.assign({}, res, {
              config: options,
              isSuccess: true
            })

            resolve(this.interceptors.response(mergeRes))
          },
          fail: (err) => {
            const mergeErr = Object.assign({}, err, {
              config: options,
              isSuccess: true
            })
            reject(this.interceptors.response(mergeErr))
          },
        })
      } else {
        // 使用 wx.request 发起请求
        wx.request({
          ...options,
          // 接口调用成功的回调函数
          success: (res) => {
            // 不管成功还是失败响应，都需要调用相应拦截器
            const morgRes = Object.assign({}, res, {
              config: options,
              isSuccess: true
            })
            resolve(this.interceptors.response(morgRes))
          },
          // 接口调用失败的回调函数
          fail: (err) => {
            // 不管成功还是失败响应，都需要调用相应拦截器
            const morgErr = Object.assign({}, err, {
              config: options,
              isSuccess: false
            })
            reject(this.interceptors.response(morgErr))
          },
          // 接口调用结束的回调函数（调用成功、失败都会执行）
          complete: () => {
            if (options.isLoading) {
              // 每次请求结束后，从队列中删除一个请求标识
              this.queue.pop()

              // 如果队列已经清空，在往队列中添加一个标识
              this.queue.length === 0 && this.queue.push('request')

              this.timerId = setTimeout(() => {
                this.queue.pop()

                this.queue.length === 0 && wx.hideLoading()
                clearTimeout(this.timerId)
              }, 1)
            }
          }
        })
      }
    })
  }

  // 封装 GET 实例方法
  get(url, data = {}, config = {}) {
    return this.request(Object.assign({
      url,
      data,
      method: 'GET'
    }, config))
  }

  // 封装 POST 实例方法
  post(url, data = {}, config = {}) {
    return this.request(Object.assign({
      url,
      data,
      method: 'POST'
    }, config))
  }

  // 封装 PUT 实例方法
  put(url, data = {}, config = {}) {
    return this.request(Object.assign({
      url,
      data,
      method: 'PUT'
    }, config))
  }

  // 封装 DELETE 实例方法
  delete(url, data = {}, config = {}) {
    return this.request(Object.assign({
      url,
      data,
      method: 'DELETE'
    }, config))
  }

  // 用来处理并发请求
  all(...primise) {
    return Promise.all(primise)
  }

  /**
   * @description 文件上传接口封装
   * @param { string } url 文件上传地址
   * @param { string } filePath 要上传文件资源的路径
   * @param { string } name 文件对应的 key
   * @param { string } config 其他配置项
   * @returns 
   */
  upload(url, filePath, name, config = {}) {
    return this.request(
      Object.assign({
        url,
        filePath,
        name,
        method: 'UPLOAD'
      }, config)
    )
  }
}

// -------------------- 实例化 -------------------- //
import {
  getStorage,
  clearStorage,
  modal,
  toast
} from './util'
import {
  env
} from './env'
const api = new WxRequest({
  baseURL: env.baseURL,
  timeout: 30000,
  isLoading: true, // 显示隐藏 loading
  // isLoading: false
})

api.interceptors.request = (config) => {

  const token = getStorage('token')
  if (token) {
    config.header['token'] = token
  }
  // 请求发送之前
  return config
}

api.interceptors.response = async (response) => {
  const {
    data,
    isSuccess
  } = response

  // 超时
  if (!isSuccess) {
    wx.toast({
      title: '网络异常请重试',
      icon: 'error'
    })
    return response
  }

  switch (data.code) {
    case 200:
      return data
    case -1:
      toast({
        title: data.msg
      })
      return Promise.reject(response)
    case 999:
      const res = await modal({
        content: data.msg,
        showCancel: false
      })
      if (res) {
        clearStorage()
        wx.navigateTo({
          url: '/pages/login/index',
        })
      }
      return Promise.reject(response)
    default:
      const res2 = await modal({
        content: "程序出现异常，是否联系梁海龙大帅哥？"
      })
      if (res2) {
        wx.makePhoneCall({
          phoneNumber: '13039326482',
        })
      }
      return Promise.reject(response)
  }
}

// 将 WxRequest 的实例通过模块化的方式暴露出去
export default api